//-------------------------------------------------------------------------------------------
// Verilog HDL model of S65NLLPLLGS_ZP1500
//-------------------------------------------------------------------------------------------
// Version: V1.2.1
// DataSheet version: V1.1.1
//
// RCS imformation:
// RCS Filename : $RCSfile: S65NLLPLLGS_ZP1500_v1.2.1.v,v $
// Updated by   : $Author: cherry $
// Checked in   : $Date: 2010/01/08 05:28:42 $
// Revision     : $Revision: 1.1 $
// State        : $State: Exp $ (Exp/Stable/Reviewed/Released etc.)
// 
//-------------------------------------------------------------------------------------------
// History:
// Version   Data       Who    What
// --------- ---------- ---- ----------------------------------------------------------------
// V1.1.1  2008/12/26  Ginger Initial Version.
// V1.2.1  2010/01/07  Ginger correct the LKDT function
//-------------------------------------------------------------------------------------------
//
//***********************************************************************************************
// Function desicription:														*
//																						*
//	   CLK_OUT  : F(Vout) = XIN*(M/N*NO)			       *
//	              IF OD is set to "00": NO=1                       *
//	              IF OD is set to "01": NO=2                       *
//	              IF OD is set to "10": NO=4                       *  
//	              IF OD is set to "11": NO=8                       *
//																						*
//        Bypass  mode 1 => CLK_OUT = F(Vout) 			       *
//		  mode 2 => CLK_OUT = XIN			       *
//																						*
//        PDRST  : used to power down analog blocks and rest digital D-flip flops 										*
//		   should be set to "0" during normal operation																				*
//																						*
//***********************************************************************************************
`timescale 1ns/10ps
`celldefine
module S65NLLPLLGS_ZP1500 ( AVDD, AVSS, DVDD, DVSS, XIN, CLK_OUT, LKDT, 
			  N, M, PDRST, OD, BP );

	inout 	AVDD;
	inout 	AVSS;
	inout   DVDD;
	inout   DVSS;
	input	XIN;			// input	
	output	CLK_OUT;		// PLL, clock out
	output  LKDT;                   // PLL, lock out 
	input	[3:0]	N;		// Input 4-bit divider control pins.
	input	[7:1]	M;		// Feed Back 7-bit divider control pins.
	input	PDRST	;                // PDRST =0 should be used in normal PLL operation.
	input	[1:0]   OD;             // Output divider control pin
	input	BP;			// PLL bypass mode selection
	

/*
	define local variables and wire
*/
        parameter       LT = 10000;
	real		m;
	real		n;
	wire		pdrst;
	wire		clk_out;
	wire		xin;
	wire		[7:1]	pll_m;
	wire		[3:0]	pll_n;
	wire		[1:0]	od;
	wire		unknown;
	wire		xinbypass;
	wire		clk_outa;
	
	reg		lkdt;
		
	real		f_vout_d;
	real		f_vout_d_pre;
	real		pre_r_time;
	real		r_delay;
	real		p_delay;
	real		x_width;
	real		dvd;
	reg		f_vout;
	reg	[8*64:1] ptrmsg;	
        reg             st_r;	
        reg             lock_free;
	reg             act;
	integer         x_cnt;
	reg		er1, er2,er3;
	
	
	
/*
	Specify timing parameter
*/

	specify
		specparam
			tr = 0.08:0.1:0.12,   // delay for rising (best, typ, worst)
			tf = 0.08:0.1:0.12,   // delay for falling (best, typ, worst)
			tb = 0.1,   	      
			td = 0.02,   	      // delay for divided mode
			bd = 0.2;	      // delay for bypass mode
	endspecify

	
	buf	(pll_m[7], M[7]);
	buf	(pll_m[6], M[6]);
	buf	(pll_m[5], M[5]);
	buf	(pll_m[4], M[4]);
	buf	(pll_m[3], M[3]);
	buf	(pll_m[2], M[2]);
	buf	(pll_m[1], M[1]);

	buf	(pll_n[3], N[3]);
	buf	(pll_n[2], N[2]);
	buf	(pll_n[1], N[1]);
	buf	(pll_n[0], N[0]);

	buf	(od[1]	, OD[1]);
	buf	(od[0]	, OD[0]);
	buf	(pdrst	, PDRST);
	buf	(xin     , XIN);
	buf	(bypass  , BP);

/*
	Calculate XIN
*/

	initial begin
		pre_r_time = $realtime;
		r_delay = 1;
		p_delay = 1;
		er1 = 1'b0;
		er2 = 1'b0;
		er3 = 1'b0;
		f_vout = 1'b0;
		m = 2;
		n = 1;
		dvd = 1;
		x_width = 1;
		f_vout_d = 10;
		x_cnt = 0;
		end

	
	
	always @ (pll_m) begin
	     if ((^pll_m === 1'bx )| (pll_m === 1'b0) | (pll_m ===1'b1)) 
		begin
		      er1 = 1'b1;
		      ptrmsg = "    Warning :  PLL_M is unknown";
		end else begin
		      m = 2*pll_m;
		      er1 = 1'b0;
		end

	end

	always @ (pll_n) begin		
		if ((|pll_n === 1'bx) | (pll_n === 1'b0) | (pll_n === 1'b1)) 
		begin
                      er2 = 1'b1;
                      ptrmsg = "    Warning :  PLL_N is unknown";
                end else begin
			n = pll_n;
			er2 = 1'b0;
		end
	end

                
	always @ ( od or pdrst or bypass ) begin
		if ((pdrst ^ pdrst) === 1'bx) begin
			er3 = 1'b1;
			ptrmsg = "    Warning : PDRST is unknown";
		end else if ((bypass ^ bypass) === 1'bx) begin
			er3 = 1'b1;
			ptrmsg = "    Warning :  BP is unknown";
		end else if (|od === 1'bx) begin
			er3 = 1'b1;
			ptrmsg = "    Warning :  OD is unknown";
		end else begin
			er3 = 1'b0;
		end
	end


	assign	unknown = er1 | er2 | er3 ;

	always @ (posedge xin) 
		begin
			r_delay    <= $realtime - pre_r_time;
			pre_r_time <= $realtime;
		end

	always @ (r_delay)
		x_width = r_delay/2.0;

	always @ (od) 
		begin
	     
			case (od)
				2'b00 : dvd = 1.0;
				2'b01 : dvd = 2.0;
				2'b10 : dvd = 4.0;
				2'b11 : dvd = 8.0;
				default: dvd = 1.0;
				
			endcase
	  
		end

       always @ (n or m or posedge act or negedge pdrst or unknown) begin
		
		if (unknown)
			st_r = 1'b0;
		else if (act)
			st_r = 1'b0;
		else if (!pdrst)
			st_r = 1'b1;
	end
      
	always @ (posedge xin) begin
		act = 1'b0;
		if (st_r && ~lock_free) begin
			$display ("success");
			x_cnt = x_cnt + 1;
			if (x_cnt == 3) begin
				lock_free = 1'b1;
				x_cnt = 0;
				act = 1'b1;
			end
		end else
			lock_free = 1'b0;
	end

		
       always @ (st_r or posedge lock_free)
	    begin
	          if (st_r == 1'b1)
		        begin
		         f_vout_d = LT;
		        end 
	          else
		    if (lock_free)	  
		     begin 
		        f_vout_d = ((n*x_width)/m) * dvd;  
                      end   
            end

	always @(dvd)
	     begin 
	       f_vout_d = ((n*x_width)/m) * dvd;  
             end   

      
	always  @ ( st_r or pdrst or unknown or bypass)
           begin
              if (unknown)
                    lkdt = 1'b0;
	      else if (pdrst) 
		    lkdt = 1'b0;
                  else if (bypass) 
                     lkdt = 1'b0;
		   else if (st_r)
	            begin		   
	            lkdt = 1'b0;		   
                    #LT;
		    lkdt = 1'b1;
	            end
	   end

	always begin
		if (unknown)
	          #1 f_vout = 1'b0;
	        else 
		#f_vout_d f_vout = ~ f_vout;
	end


	
	assign #bd xinbypass = xin;
	assign #(tr,tf,0) clk_outa = (bypass) ? xinbypass : f_vout;
	assign clk_out =(unknown) ? 1'bx : ((pdrst)? 1'b0 : clk_outa);
	
			

	buf #tb (CLK_OUT,clk_out);
        buf (LKDT,lkdt);
endmodule
`endcelldefine

